package soko.control;

import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;
import soko.sokoban.*;

/**
 * Class to parse a screen file.
 */
public class Parser {

	private File inputFile;

	/**
	 * Constructs a new Parser.
	 *
	 * @param inputFile path to a screen file.
	 */
	public Parser(String inputFile) {
		this.inputFile = new File(inputFile);
	}

	/**
	 * Gets an ArrayList of Tiles.
	 *
	 * @return an ArrayList of Tiles
	 * @throws FileNotFoundException if the screen file is not found.
	 * @throws CharNotFoundException if a char in a screen is invalid.
	 * @throws InvalidBoardException if a board is not valid, e.g. it contains
	 * no goal.
	 */
	public Tiles getScreen() throws FileNotFoundException, CharNotFoundException, InvalidBoardException {
		Tiles tiles = new Tiles();
		Scanner scan = new Scanner(inputFile);
		int y = 0;
		int i = 0;
		int blockCount = 0;
		int goalCount = 0;
		int manCount = 0;
		while (scan.hasNextLine()) {
			String line = scan.nextLine();
			for (int x = 0; x < line.length(); x++) {
				Representation repr = Representation.getRepresentation(line.charAt(x));
				if (repr != null) {
					if (repr == Representation.BLOCK || repr == Representation.BLOCK_ON_GOAL) {
						blockCount++;
					}

					if (repr == Representation.GOAL || repr == Representation.BLOCK_ON_GOAL) {
						goalCount++;
					}

					if (repr == Representation.MAN || repr == Representation.MAN_ON_GOAL) {
						manCount++;
					}
					tiles.add(new Tile(repr, x, y, i));
					i++;
				}
			}
			y++;
		}

		if (blockCount == 0 || goalCount == 0 || blockCount != goalCount || manCount != 1) {
			throw new InvalidBoardException();
		}

		setNeighbours(tiles);

		return tiles;
	}

	/**
	 * Sets all neighbours of all tiles. Such that Tile.getUpTile() etc. can be called.
	 * 
	 * @param tiles an ArrayList of Tiles.
	 */
	private void setNeighbours(Tiles tiles) {
		for (Tile tile : tiles) {
			//left
			tile.setLeftTile(tiles.getTile(tile.getX() - 1, tile.getY()));

			//right
			tile.setRightTile(tiles.getTile(tile.getX() + 1, tile.getY()));

			//up
			tile.setUpTile(tiles.getTile(tile.getX(), tile.getY() - 1));

			//down
			tile.setDownTile(tiles.getTile(tile.getX(), tile.getY() + 1));
		}
	}
}